/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pt.iasd.youthshelter.datacore.controller;

import java.io.Serializable;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import pt.iasd.youthshelter.datamodel.Church;
import pt.iasd.youthshelter.datamodel.GraduationSchool;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import org.openide.util.Lookup;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.openide.util.lookup.ServiceProvider;
import pt.iasd.youthshelter.datacore.EntityManagerSemaphore;
import pt.iasd.youthshelter.datacore.controller.exceptions.IllegalOrphanException;
import pt.iasd.youthshelter.datacore.controller.exceptions.NonexistentEntityException;
import pt.iasd.youthshelter.datacore.events.PathfinderRefreshEvent;
import pt.iasd.youthshelter.datamodel.PathfinderRole;
import pt.iasd.youthshelter.datamodel.Investiture;
import pt.iasd.youthshelter.datamodel.ActivityParticipation;
import pt.iasd.youthshelter.datamodel.Pathfinder;
import pt.iasd.youthshelter.datamodel.service.PathfinderController;

/**
 *
 * @author Rui
 */
@ServiceProvider(service=PathfinderController.class)
public class PathfinderJpaController implements Serializable, PathfinderController {
    private InstanceContent content = new InstanceContent();
    private Lookup lookup = new AbstractLookup(content);

    private EntityManager aquireEntityManager() throws InterruptedException {
        EntityManagerSemaphore.getInstance().getSemaphore().acquire();
        return EntityManagerSemaphore.getInstance().getEntityManager();
    }
    
    private void releaseEntityManager(EntityManager em) {
        if (em != null && em.isOpen()) {
            em.close();
        }
        EntityManagerSemaphore.getInstance().getSemaphore().release();
    }

    @Override
    public void create(Pathfinder pathfinder) {
        if (pathfinder.getGraduationSchoolList() == null) {
            pathfinder.setGraduationSchoolList(new ArrayList<GraduationSchool>());
        }
        if (pathfinder.getPathfinderRoleList() == null) {
            pathfinder.setPathfinderRoleList(new ArrayList<PathfinderRole>());
        }
        if (pathfinder.getInvestitureList() == null) {
            pathfinder.setInvestitureList(new ArrayList<Investiture>());
        }
        if (pathfinder.getActivityParticipationList() == null) {
            pathfinder.setActivityParticipationList(new ArrayList<ActivityParticipation>());
        }
        EntityManager em = null;
        try {
            em = aquireEntityManager();
            em.getTransaction().begin();
            Church churchId = pathfinder.getChurchId();
            if (churchId != null) {
                churchId = em.getReference(churchId.getClass(), churchId.getId());
                pathfinder.setChurchId(churchId);
            }
            List<GraduationSchool> attachedGraduationSchoolList = new ArrayList<GraduationSchool>();
            for (GraduationSchool graduationSchoolListGraduationSchoolToAttach : pathfinder.getGraduationSchoolList()) {
                graduationSchoolListGraduationSchoolToAttach = em.getReference(graduationSchoolListGraduationSchoolToAttach.getClass(), graduationSchoolListGraduationSchoolToAttach.getId());
                attachedGraduationSchoolList.add(graduationSchoolListGraduationSchoolToAttach);
            }
            pathfinder.setGraduationSchoolList(attachedGraduationSchoolList);
            List<PathfinderRole> attachedPathfinderRoleList = new ArrayList<PathfinderRole>();
            for (PathfinderRole pathfinderRoleListPathfinderRoleToAttach : pathfinder.getPathfinderRoleList()) {
                pathfinderRoleListPathfinderRoleToAttach = em.getReference(pathfinderRoleListPathfinderRoleToAttach.getClass(), pathfinderRoleListPathfinderRoleToAttach.getId());
                attachedPathfinderRoleList.add(pathfinderRoleListPathfinderRoleToAttach);
            }
            pathfinder.setPathfinderRoleList(attachedPathfinderRoleList);
            List<Investiture> attachedInvestitureList = new ArrayList<Investiture>();
            for (Investiture investitureListInvestitureToAttach : pathfinder.getInvestitureList()) {
                investitureListInvestitureToAttach = em.getReference(investitureListInvestitureToAttach.getClass(), investitureListInvestitureToAttach.getId());
                attachedInvestitureList.add(investitureListInvestitureToAttach);
            }
            pathfinder.setInvestitureList(attachedInvestitureList);
            List<ActivityParticipation> attachedActivityParticipationList = new ArrayList<ActivityParticipation>();
            for (ActivityParticipation activityParticipationListActivityParticipationToAttach : pathfinder.getActivityParticipationList()) {
                activityParticipationListActivityParticipationToAttach = em.getReference(activityParticipationListActivityParticipationToAttach.getClass(), activityParticipationListActivityParticipationToAttach.getActivityParticipationPK());
                attachedActivityParticipationList.add(activityParticipationListActivityParticipationToAttach);
            }
            pathfinder.setActivityParticipationList(attachedActivityParticipationList);
            em.persist(pathfinder);
            if (churchId != null) {
                churchId.getPathfinderList().add(pathfinder);
                em.merge(churchId);
            }
            for (GraduationSchool graduationSchoolListGraduationSchool : pathfinder.getGraduationSchoolList()) {
                graduationSchoolListGraduationSchool.getPathfinderList().add(pathfinder);
                graduationSchoolListGraduationSchool = em.merge(graduationSchoolListGraduationSchool);
            }
            for (PathfinderRole pathfinderRoleListPathfinderRole : pathfinder.getPathfinderRoleList()) {
                Pathfinder oldPathfinderIdOfPathfinderRoleListPathfinderRole = pathfinderRoleListPathfinderRole.getPathfinderId();
                pathfinderRoleListPathfinderRole.setPathfinderId(pathfinder);
                pathfinderRoleListPathfinderRole = em.merge(pathfinderRoleListPathfinderRole);
                if (oldPathfinderIdOfPathfinderRoleListPathfinderRole != null) {
                    oldPathfinderIdOfPathfinderRoleListPathfinderRole.getPathfinderRoleList().remove(pathfinderRoleListPathfinderRole);
                    em.merge(oldPathfinderIdOfPathfinderRoleListPathfinderRole);
                }
            }
            for (Investiture investitureListInvestiture : pathfinder.getInvestitureList()) {
                Pathfinder oldPathfinderIdOfInvestitureListInvestiture = investitureListInvestiture.getPathfinderId();
                investitureListInvestiture.setPathfinderId(pathfinder);
                investitureListInvestiture = em.merge(investitureListInvestiture);
                if (oldPathfinderIdOfInvestitureListInvestiture != null) {
                    oldPathfinderIdOfInvestitureListInvestiture.getInvestitureList().remove(investitureListInvestiture);
                    em.merge(oldPathfinderIdOfInvestitureListInvestiture);
                }
            }
            for (ActivityParticipation activityParticipationListActivityParticipation : pathfinder.getActivityParticipationList()) {
                Pathfinder oldPathfinderOfActivityParticipationListActivityParticipation = activityParticipationListActivityParticipation.getPathfinder();
                activityParticipationListActivityParticipation.setPathfinder(pathfinder);
                activityParticipationListActivityParticipation = em.merge(activityParticipationListActivityParticipation);
                if (oldPathfinderOfActivityParticipationListActivityParticipation != null) {
                    oldPathfinderOfActivityParticipationListActivityParticipation.getActivityParticipationList().remove(activityParticipationListActivityParticipation);
                    em.merge(oldPathfinderOfActivityParticipationListActivityParticipation);
                }
            }
            em.getTransaction().commit();
        } catch(InterruptedException ex) {
        } finally {
            releaseEntityManager(em);
        }
        content.set(Arrays.asList(new PathfinderRefreshEvent()), null);
    }

    @Override
    public void edit(Pathfinder pathfinder) throws IllegalOrphanException, NonexistentEntityException, Exception {
        EntityManager em = null;
        try {
            em = aquireEntityManager();
            em.getTransaction().begin();
            Pathfinder persistentPathfinder = em.find(Pathfinder.class, pathfinder.getId());
            Church churchIdOld = persistentPathfinder.getChurchId();
            Church churchIdNew = pathfinder.getChurchId();
            List<GraduationSchool> graduationSchoolListOld = persistentPathfinder.getGraduationSchoolList();
            List<GraduationSchool> graduationSchoolListNew = pathfinder.getGraduationSchoolList();
            List<PathfinderRole> pathfinderRoleListOld = persistentPathfinder.getPathfinderRoleList();
            List<PathfinderRole> pathfinderRoleListNew = pathfinder.getPathfinderRoleList();
            List<Investiture> investitureListOld = persistentPathfinder.getInvestitureList();
            List<Investiture> investitureListNew = pathfinder.getInvestitureList();
            List<ActivityParticipation> activityParticipationListOld = persistentPathfinder.getActivityParticipationList();
            List<ActivityParticipation> activityParticipationListNew = pathfinder.getActivityParticipationList();
            List<String> illegalOrphanMessages = null;
            for (PathfinderRole pathfinderRoleListOldPathfinderRole : pathfinderRoleListOld) {
                if (!pathfinderRoleListNew.contains(pathfinderRoleListOldPathfinderRole)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain PathfinderRole " + pathfinderRoleListOldPathfinderRole + " since its pathfinderId field is not nullable.");
                }
            }
            for (Investiture investitureListOldInvestiture : investitureListOld) {
                if (!investitureListNew.contains(investitureListOldInvestiture)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Investiture " + investitureListOldInvestiture + " since its pathfinderId field is not nullable.");
                }
            }
            for (ActivityParticipation activityParticipationListOldActivityParticipation : activityParticipationListOld) {
                if (!activityParticipationListNew.contains(activityParticipationListOldActivityParticipation)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain ActivityParticipation " + activityParticipationListOldActivityParticipation + " since its pathfinder field is not nullable.");
                }
            }
            if (illegalOrphanMessages != null) {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            if (churchIdNew != null) {
                churchIdNew = em.getReference(churchIdNew.getClass(), churchIdNew.getId());
                pathfinder.setChurchId(churchIdNew);
            }
            List<GraduationSchool> attachedGraduationSchoolListNew = new ArrayList<GraduationSchool>();
            for (GraduationSchool graduationSchoolListNewGraduationSchoolToAttach : graduationSchoolListNew) {
                graduationSchoolListNewGraduationSchoolToAttach = em.getReference(graduationSchoolListNewGraduationSchoolToAttach.getClass(), graduationSchoolListNewGraduationSchoolToAttach.getId());
                attachedGraduationSchoolListNew.add(graduationSchoolListNewGraduationSchoolToAttach);
            }
            graduationSchoolListNew = attachedGraduationSchoolListNew;
            pathfinder.setGraduationSchoolList(graduationSchoolListNew);
            List<PathfinderRole> attachedPathfinderRoleListNew = new ArrayList<PathfinderRole>();
            for (PathfinderRole pathfinderRoleListNewPathfinderRoleToAttach : pathfinderRoleListNew) {
                pathfinderRoleListNewPathfinderRoleToAttach = em.getReference(pathfinderRoleListNewPathfinderRoleToAttach.getClass(), pathfinderRoleListNewPathfinderRoleToAttach.getId());
                attachedPathfinderRoleListNew.add(pathfinderRoleListNewPathfinderRoleToAttach);
            }
            pathfinderRoleListNew = attachedPathfinderRoleListNew;
            pathfinder.setPathfinderRoleList(pathfinderRoleListNew);
            List<Investiture> attachedInvestitureListNew = new ArrayList<Investiture>();
            for (Investiture investitureListNewInvestitureToAttach : investitureListNew) {
                investitureListNewInvestitureToAttach = em.getReference(investitureListNewInvestitureToAttach.getClass(), investitureListNewInvestitureToAttach.getId());
                attachedInvestitureListNew.add(investitureListNewInvestitureToAttach);
            }
            investitureListNew = attachedInvestitureListNew;
            pathfinder.setInvestitureList(investitureListNew);
            List<ActivityParticipation> attachedActivityParticipationListNew = new ArrayList<ActivityParticipation>();
            for (ActivityParticipation activityParticipationListNewActivityParticipationToAttach : activityParticipationListNew) {
                activityParticipationListNewActivityParticipationToAttach = em.getReference(activityParticipationListNewActivityParticipationToAttach.getClass(), activityParticipationListNewActivityParticipationToAttach.getActivityParticipationPK());
                attachedActivityParticipationListNew.add(activityParticipationListNewActivityParticipationToAttach);
            }
            activityParticipationListNew = attachedActivityParticipationListNew;
            pathfinder.setActivityParticipationList(activityParticipationListNew);
            pathfinder = em.merge(pathfinder);
            if (churchIdOld != null && !churchIdOld.equals(churchIdNew)) {
                churchIdOld.getPathfinderList().remove(pathfinder);
                churchIdOld = em.merge(churchIdOld);
            }
            if (churchIdNew != null && !churchIdNew.equals(churchIdOld)) {
                churchIdNew.getPathfinderList().add(pathfinder);
                em.merge(churchIdNew);
            }
            for (GraduationSchool graduationSchoolListOldGraduationSchool : graduationSchoolListOld) {
                if (!graduationSchoolListNew.contains(graduationSchoolListOldGraduationSchool)) {
                    graduationSchoolListOldGraduationSchool.getPathfinderList().remove(pathfinder);
                    graduationSchoolListOldGraduationSchool = em.merge(graduationSchoolListOldGraduationSchool);
                }
            }
            for (GraduationSchool graduationSchoolListNewGraduationSchool : graduationSchoolListNew) {
                if (!graduationSchoolListOld.contains(graduationSchoolListNewGraduationSchool)) {
                    graduationSchoolListNewGraduationSchool.getPathfinderList().add(pathfinder);
                    graduationSchoolListNewGraduationSchool = em.merge(graduationSchoolListNewGraduationSchool);
                }
            }
            for (PathfinderRole pathfinderRoleListNewPathfinderRole : pathfinderRoleListNew) {
                if (!pathfinderRoleListOld.contains(pathfinderRoleListNewPathfinderRole)) {
                    Pathfinder oldPathfinderIdOfPathfinderRoleListNewPathfinderRole = pathfinderRoleListNewPathfinderRole.getPathfinderId();
                    pathfinderRoleListNewPathfinderRole.setPathfinderId(pathfinder);
                    pathfinderRoleListNewPathfinderRole = em.merge(pathfinderRoleListNewPathfinderRole);
                    if (oldPathfinderIdOfPathfinderRoleListNewPathfinderRole != null && !oldPathfinderIdOfPathfinderRoleListNewPathfinderRole.equals(pathfinder)) {
                        oldPathfinderIdOfPathfinderRoleListNewPathfinderRole.getPathfinderRoleList().remove(pathfinderRoleListNewPathfinderRole);
                        em.merge(oldPathfinderIdOfPathfinderRoleListNewPathfinderRole);
                    }
                }
            }
            for (Investiture investitureListNewInvestiture : investitureListNew) {
                if (!investitureListOld.contains(investitureListNewInvestiture)) {
                    Pathfinder oldPathfinderIdOfInvestitureListNewInvestiture = investitureListNewInvestiture.getPathfinderId();
                    investitureListNewInvestiture.setPathfinderId(pathfinder);
                    investitureListNewInvestiture = em.merge(investitureListNewInvestiture);
                    if (oldPathfinderIdOfInvestitureListNewInvestiture != null && !oldPathfinderIdOfInvestitureListNewInvestiture.equals(pathfinder)) {
                        oldPathfinderIdOfInvestitureListNewInvestiture.getInvestitureList().remove(investitureListNewInvestiture);
                        em.merge(oldPathfinderIdOfInvestitureListNewInvestiture);
                    }
                }
            }
            for (ActivityParticipation activityParticipationListNewActivityParticipation : activityParticipationListNew) {
                if (!activityParticipationListOld.contains(activityParticipationListNewActivityParticipation)) {
                    Pathfinder oldPathfinderOfActivityParticipationListNewActivityParticipation = activityParticipationListNewActivityParticipation.getPathfinder();
                    activityParticipationListNewActivityParticipation.setPathfinder(pathfinder);
                    activityParticipationListNewActivityParticipation = em.merge(activityParticipationListNewActivityParticipation);
                    if (oldPathfinderOfActivityParticipationListNewActivityParticipation != null && !oldPathfinderOfActivityParticipationListNewActivityParticipation.equals(pathfinder)) {
                        oldPathfinderOfActivityParticipationListNewActivityParticipation.getActivityParticipationList().remove(activityParticipationListNewActivityParticipation);
                        em.merge(oldPathfinderOfActivityParticipationListNewActivityParticipation);
                    }
                }
            }
            em.getTransaction().commit();
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Integer id = pathfinder.getId();
                if (findPathfinder(id) == null) {
                    throw new NonexistentEntityException("The pathfinder with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            releaseEntityManager(em);
        }
        content.set(Arrays.asList(new PathfinderRefreshEvent()), null);
    }

    @Override
    public void destroy(Integer id) throws IllegalOrphanException, NonexistentEntityException {
        EntityManager em = null;
        try {
            em = aquireEntityManager();
            em.getTransaction().begin();
            Pathfinder pathfinder;
            try {
                pathfinder = em.getReference(Pathfinder.class, id);
                pathfinder.getId();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The pathfinder with id " + id + " no longer exists.", enfe);
            }
            List<String> illegalOrphanMessages = null;
            List<PathfinderRole> pathfinderRoleListOrphanCheck = pathfinder.getPathfinderRoleList();
            for (PathfinderRole pathfinderRoleListOrphanCheckPathfinderRole : pathfinderRoleListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Pathfinder (" + pathfinder + ") cannot be destroyed since the PathfinderRole " + pathfinderRoleListOrphanCheckPathfinderRole + " in its pathfinderRoleList field has a non-nullable pathfinderId field.");
            }
            List<Investiture> investitureListOrphanCheck = pathfinder.getInvestitureList();
            for (Investiture investitureListOrphanCheckInvestiture : investitureListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Pathfinder (" + pathfinder + ") cannot be destroyed since the Investiture " + investitureListOrphanCheckInvestiture + " in its investitureList field has a non-nullable pathfinderId field.");
            }
            List<ActivityParticipation> activityParticipationListOrphanCheck = pathfinder.getActivityParticipationList();
            for (ActivityParticipation activityParticipationListOrphanCheckActivityParticipation : activityParticipationListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Pathfinder (" + pathfinder + ") cannot be destroyed since the ActivityParticipation " + activityParticipationListOrphanCheckActivityParticipation + " in its activityParticipationList field has a non-nullable pathfinder field.");
            }
            if (illegalOrphanMessages != null) {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            Church churchId = pathfinder.getChurchId();
            if (churchId != null) {
                churchId.getPathfinderList().remove(pathfinder);
                em.merge(churchId);
            }
            List<GraduationSchool> graduationSchoolList = pathfinder.getGraduationSchoolList();
            for (GraduationSchool graduationSchoolListGraduationSchool : graduationSchoolList) {
                graduationSchoolListGraduationSchool.getPathfinderList().remove(pathfinder);
                graduationSchoolListGraduationSchool = em.merge(graduationSchoolListGraduationSchool);
            }
            em.remove(pathfinder);
            em.getTransaction().commit();
        } catch(InterruptedException ex) {
        } finally {
            releaseEntityManager(em);
        }
        content.set(Arrays.asList(new PathfinderRefreshEvent()), null);
    }

    @Override
    public List<Pathfinder> findPathfinderEntities() {
        return findPathfinderEntities(true, -1, -1);
    }

    @Override
    public List<Pathfinder> findPathfinderEntities(int maxResults, int firstResult) {
        return findPathfinderEntities(false, maxResults, firstResult);
    }

    private List<Pathfinder> findPathfinderEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = null;
        try {
            em = aquireEntityManager();
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(Pathfinder.class));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } catch(InterruptedException ex) {
        } finally {
            releaseEntityManager(em);
        }
        return null;
    }

    @Override
    public List<Pathfinder> findActivePathfinderEntities() {
        return findActivePathfinderEntities(true, -1, -1);
    }

    @Override
    public List<Pathfinder> findActivePathfinderEntities(int maxResults, int firstResult) {
        return findActivePathfinderEntities(false, maxResults, firstResult);
    }
    
    private List<Pathfinder> findActivePathfinderEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = null;
        try {
            em = aquireEntityManager();
            CriteriaBuilder cb = em.getCriteriaBuilder();
            CriteriaQuery cq = cb.createQuery();
            Root<Pathfinder> root = cq.from(Pathfinder.class);
            cq.select(root);
            cq.where(cb.isNull(root.get(root.getModel().getSingularAttribute("endActive", Date.class))));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } catch(InterruptedException ex) {
        } finally {
            releaseEntityManager(em);
        }
        return null;
    }

    @Override
    public Pathfinder findPathfinder(Integer id) {
        EntityManager em = null;
        try {
            em = aquireEntityManager();
            return em.find(Pathfinder.class, id);
        } catch(InterruptedException ex) {
        } finally {
            releaseEntityManager(em);
        }
        return null;
    }

    @Override
    public int getPathfinderCount() {
        EntityManager em = null;
        try {
            em = aquireEntityManager();
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<Pathfinder> rt = cq.from(Pathfinder.class);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } catch(InterruptedException ex) {
        } finally {
            releaseEntityManager(em);
        }
        return 0;
    }

    @Override
    public Lookup getLookup() {
        return lookup;
    }
}
